package com.selftcp;

import java.util.ArrayList;
import java.util.List;

//模拟TCP数据包
public class TCPacket {
    int sourcePort;
    int destPort;
    long seq;       // 序列号
    long ack;       // 确认号
    boolean syn;    // 同步标志
    boolean ackFlag;// 确认标志
    boolean fin;    // 结束标志
    int window;     // 窗口大小
    byte[] data;    // 数据

    public TCPacket(int sourcePort, int destPort, long seq, long ack, 
                   boolean syn, boolean ackFlag, boolean fin, int window, byte[] data) {
        this.sourcePort = sourcePort;
        this.destPort = destPort;
        this.seq = seq;
        this.ack = ack;
        this.syn = syn;
        this.ackFlag = ackFlag;
        this.fin = fin;
        this.window = window;
        this.data = data != null ? data.clone() : new byte[0];
    }
    
    // 序列化数据包
    public byte[] toBytes() {
        List<Byte> bytes = new ArrayList<>();
        
        // 端口（各2字节）
        addShort(bytes, (short) sourcePort);
        addShort(bytes, (short) destPort);
        
        // 序列号和确认号（各8字节）
        addLong(bytes, seq);
        addLong(bytes, ack);
        
        // 控制位（1字节）
        byte flags = 0;
        if (syn) flags |= 0b100;
        if (ackFlag) flags |= 0b010;
        if (fin) flags |= 0b001;
        bytes.add(flags);
        
        // 窗口大小（2字节）
        addShort(bytes, (short) window);
        
        // 数据长度（2字节）- 仅用于传输，应用层不处理
        addShort(bytes, (short) (data != null ? data.length : 0));
        
        // 数据
        if (data != null) {
            for (byte b : data) {
                bytes.add(b);
            }
        }
        
        // 转换为byte数组
        byte[] result = new byte[bytes.size()];
        for (int i = 0; i < result.length; i++) {
            result[i] = bytes.get(i);
        }
        return result;
    }
    
    // 从字节数组解析数据包
    public static TCPacket fromBytes(byte[] data) {
        int ptr = 0;
        
        int sourcePort = getShort(data, ptr);
        ptr += 2;
        
        int destPort = getShort(data, ptr);
        ptr += 2;
        
        long seq = getLong(data, ptr);
        ptr += 8;
        
        long ack = getLong(data, ptr);
        ptr += 8;
        
        byte flags = data[ptr++];
        boolean syn = (flags & 0b100) != 0;
        boolean ackFlag = (flags & 0b010) != 0;
        boolean fin = (flags & 0b001) != 0;
        
        int window = getShort(data, ptr);
        ptr += 2;
        
        int dataLen = getShort(data, ptr);
        ptr += 2;
        
        byte[] payload = new byte[dataLen];
        if (dataLen > 0 && ptr + dataLen <= data.length) {
            System.arraycopy(data, ptr, payload, 0, dataLen);
        }
        
        return new TCPacket(sourcePort, destPort, seq, ack, syn, ackFlag, fin, window, payload);
    }
    
    private static void addShort(List<Byte> list, short value) {
        list.add((byte) (value >> 8));
        list.add((byte) (value & 0xFF));
    }
    
    private static void addLong(List<Byte> list, long value) {
        for (int i = 7; i >= 0; i--) {
            list.add((byte) (value >> (i * 8)));
        }
    }
    
    private static int getShort(byte[] data, int offset) {
        if (offset + 1 >= data.length) return 0;
        return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
    }
    
    private static long getLong(byte[] data, int offset) {
        if (offset + 7 >= data.length) return 0;
        long result = 0;
        for (int i = 0; i < 8; i++) {
            result = (result << 8) | (data[offset + i] & 0xFF);
        }
        return result;
    }

	@Override
	public String toString() {
		return "TCPacket [sourcePort=" + sourcePort + ", destPort=" + destPort + ", seq=" + seq + ", ack=" + ack
				+ ", syn=" + syn + ", ackFlag=" + ackFlag + ", fin=" + fin + ", window=" + window + ", data=" + data.length + "]";
	}
    
}